package org.goldenport.g3

import scala.tools.nsc.{ScriptRunner, GenericRunnerSettings, Interpreter}
import scala.tools.nsc.util.BatchSourceFile
import scala.collection.mutable
import scala.collection.mutable.{Stack, ArrayBuffer, LinkedHashMap, HashMap, MultiMap}
import scala.collection.JavaConversions.enumerationAsScalaIterator
import scala.xml.{Elem, Node}
import java.io.{InputStream, IOException}
import java.lang.reflect.{Method, Modifier}
import javax.servlet.ServletConfig
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpServletResponse.SC_OK
import com.asamioffice.goldenport.util.{ CommandParameterParser, UserPropertyParser, WorkPropertyParser, ResourcePropertyParser, IParameterParser }
import com.asamioffice.goldenport.util.OptionUtil.lift
import org.goldenport.parameter.GParameterRepository
import org.goldenport.session.GSession
import org.goldenport.service.GServiceContext
import org.goldenport.g3.jetty.JettyServer
import org.goldenport.g3.platforms.{StandalonePlatform, GoldenportPlatform, AppEnginePlatform}
import org.goldenport.g3.atom.{AtomFeed, AtomEntry}
import org.goldenport.g3.messages._
import org.goldenport.g3.messages.cli.CliCommand
import org.goldenport.g3.messages.http.HttpCommand
import org.goldenport.g3.events.{Getted, RestEvent}
import org.goldenport.g3.events.http._
import scalaz._
import Scalaz._
import org.goldenport.g3.messages.RecordField
import org.goldenport.g3.messages.XBase64Binary

// GoldenportBus Feb. 20, 2010
/**
 * @since   Mar. 19, 2010
 * @version Dec.  6, 2011
 * @author  ASAMI, Tomoharu
 */
object Main {
  def main(args: Array[String]) {
    val (app, params, interpreter) = find_application(args.toList)
    app.open(params, interpreter)
    app.run(params: _*)
    app.close()
  }

  import org.goldenport.util.Control.orElseN

  private def find_application(args: List[String]): (G3Application, List[String], Interpreter) = {
    val settings = new GenericRunnerSettings((error: String) => println(error))
    val ps = settings.processArguments(args, true)._2
    settings.nocompdaemon.value = true // XXX
    val ip = new Interpreter(settings)
    val app = orElseN(exec_initial_script _, find_application_appcp _) {
      new G3Application()
    } (ps, ip)
    (app, ps, ip)
  }

  private def exec_initial_script(params: List[String], interpreter: Interpreter): Option[G3Application] = {
    def get_app_name = {
      params.find(_.startsWith("-g3.application:")) match {
        case Some(app) => app.substring("-g3.application:".length)
        case None => "App"
      }
    }

    params.find(_.startsWith("-g3.script.app:")) match {
      case Some(op) => {
        val filename = op.substring(op.indexOf(":") + 1)
        val file = new scala.tools.nsc.io.PlainFile(scala.tools.nsc.io.File(new java.io.File(filename)))
        interpreter.compileSources(new BatchSourceFile(file))
        val appname = get_app_name
        val cl = interpreter.classLoader
        val klass = cl.loadClass(appname)
        Some(klass.newInstance.asInstanceOf[G3Application])
      }
      case None => None
    }
  }

  private def find_application_appcp(params: List[String], interpreter: Interpreter): Option[G3Application] = {
    params.find(_.startsWith("-g3.application:")) match {
      case Some(app) => {
        val classname = app.substring("-g3.application:".length)
        val klass = params.find(_.startsWith("-g3.classpath:")) match {
          case Some(appcp) => error("not supported yet")
          case None => Class.forName(classname)
        }
        Some(klass.newInstance.asInstanceOf[G3Application])
      }
      case None => None
    }
  }

/*
  private def script(args: Array[String]) {
    val settings = new GenericRunnerSettings((error: String) => println(error))
    println("process = " + settings.processArguments(args.toList, true))
    settings.nocompdaemon.value = true
    println("args = " + args.mkString(","))
    println("G3App = " + settings.classpath.value)
    val interpreter = new Interpreter(settings)
    interpreter.interpret("""import org.goldenport.g3._""")
    interpreter.interpret("""println("hello")""")
    println("G3: script")
    val file = new scala.tools.nsc.io.PlainFile(scala.tools.nsc.io.File(new java.io.File("etc/hello.scala")))
    interpreter.compileSources(new BatchSourceFile(file))
    interpreter.interpret("""new G3Hello""")
    println("G3: script end")
  }

  private def script0(args: Array[String]) {
    val settings = new GenericRunnerSettings((error: String) => println(error))
    settings.nocompdaemon.value = true
    println("G3App = " + settings.classpath.value)
    ScriptRunner.runScript(settings, "etc/hello.scala", Nil)
  }
*/
}

class G3Application() extends G3Module {
  private var param_server = false
  private var _application_session: Option[G3Session] = None

  init_context(new RootContext())

  // from Application
  def open(args: String*) {
    val settings = new GenericRunnerSettings((error: String) => println(error))
    val processed_args = settings.processArguments(args.toList, true)
    settings.nocompdaemon.value = true // XXX
    val interpreter = new Interpreter(settings)
    open(processed_args._2, interpreter)
  }

  // from Main
  def open(args: List[String], interpreter: Interpreter) {
    val params = new GParameterRepository
    setup_resource_config(params)
    setup_config(params)
    set_properties(params, new CommandParameterParser(args.toArray)) // XXX
    val ctx = new InterpreterContext(interpreter)
    push_context(ctx)
    ctx.setPlatform(new StandalonePlatform)
    open_application(params)
  }

  private def set_properties(params: GParameterRepository,
                             options: IParameterParser) {
    for (option <- options.getOptions) {
//        println("key, vaue = " + option.getKey + "/" + option.getValue)
      params.put(option.getKey, option.getValue)
    }
  }

  private def setup_config(params: GParameterRepository) {
    set_properties(params, new UserPropertyParser(".g3.properties"))
    set_properties(params, new UserPropertyParser("g3.properties"))
    set_properties(params, new WorkPropertyParser(".g3.properties"))
    set_properties(params, new WorkPropertyParser("g3.properties"))
  }

  private def setup_resource_config(params: GParameterRepository) {
    set_properties(params, new ResourcePropertyParser("/META-INF/g3.properties", this))
    set_properties(params, new ResourcePropertyParser("/WEB-INF/g3.properties", this))
    set_properties(params, new ResourcePropertyParser("/war/WEB-INF/g3.properties", this))
  }

  // from GService
  def open(parameters: GParameterRepository, session: GSession,
           gcontext: GServiceContext, ip: Interpreter) {
    setup_resource_config(parameters)
    setup_config(parameters)
    val ctx = new GoldenportContext(ip, session, gcontext)
    push_context(ctx)
    ctx.setPlatform(new GoldenportPlatform(session, gcontext))
    open_application(parameters)
  }

  // from TimerServer
  def openTimer() {
    val params = new GParameterRepository
    setup_resource_config(params)
    setup_config(params)
    val ctx = new PlainContext()
    push_context(ctx)
    ctx.setPlatform(new StandalonePlatform())
    open_application(params)
  }

  // from Servlet
  def openServlet(config: ServletConfig) {
    val params = new GParameterRepository
    setup_resource_config(params)
    setup_servlet_config(params, config)
    setup_config(params)
    val ctx = new PlainContext()
    push_context(ctx)
    ctx.setPlatform(new StandalonePlatform())
    open_application(params)
  }

  // from AppEngine
  def openAppEngine(config: ServletConfig) {
    val params = new GParameterRepository
    setup_resource_config(params)
    setup_servlet_config(params, config)
    params.put("g3.datastore", "appengine") // XXX
    val ctx = new AppEngineContext()
    push_context(ctx)
    ctx.setPlatform(new AppEnginePlatform())
    open_application(params)
  }

  private def setup_servlet_config(params: GParameterRepository, config: ServletConfig) {
    for (n <- config.getInitParameterNames; name = n.toString) {
//      println("G3Application: setup_servlet_config: " + name + ":" + config.getInitParameter(name))
      params.put(name, config.getInitParameter(name))
    }
  }

  private def open_application(parameters: GParameterRepository) {
    context.setTitle(getClass.getSimpleName)
    param_server = get_parameter_boolean(parameters, "g3.server", false)
    for (value <- parameters.getString("g3.datastore")) {
      context.setDefaultDatastore(value)
    }
    for (value <- parameters.getString("g3.http.format")) {
      context.setDefaultHttpFormat(value)
    }
//    println("G3Application: " + parameters.getString("g3.http.format"))
//    println("G3Application: " + context.getDefaultHttpFormat)

    for (user <- parameters.getString("g3.auth.google.user")) {
      val password = get_parameter_string(parameters, "g3.auth.google.password")
      context.putAuthentication("http://google.com/", new BasicAuthentication(user, password))
    }
    for (user <- parameters.getString("g3.auth.twitter.user")) {
//      println("g3.auth.twitter.user =" + user)
      val password = get_parameter_string(parameters, "g3.auth.twitter.password")
      val ckey = get_parameter_string(parameters, "g3.auth.twitter.consumerKey")
      val csecret = get_parameter_string(parameters, "g3.auth.twitter.consumerSecret")
      val atoken = get_parameter_string(parameters, "g3.auth.twitter.accessToken")
      val atsecret = get_parameter_string(parameters, "g3.auth.twitter.accessTokenSecret")
      val auth = if (ckey != "") {
        new OAuthAuthentication(user, ckey, csecret, Some(atoken), Some(atsecret))
      } else {
        new BasicAuthentication(user, password)
      }
      context.putAuthentication("http://twitter.com/", auth)
    }
    for (user <- parameters.getString("g3.auth.evernote.user")) {
      val password = get_parameter_string(parameters, "g3.auth.evernote.password")
      context.putAuthentication("http://evernote.com/", new BasicAuthentication(user, password))
    }
    for (user <- parameters.getString("g3.auth.dropbox.user")) {
      val password = get_parameter_string(parameters, "g3.auth.dropbox.password")
      context.putAuthentication("http://dropbox.com/", new BasicAuthentication(user, password))
    }
    for (user <- parameters.getString("g3.auth.jdbc.user")) {
      val password = get_parameter_string(parameters, "g3.auth.jdbc.password")
      context.putAuthentication("jdbc://", new BasicAuthentication(user, password))
    }
    for (driver <- parameters.getString("g3.jdbc.driver")) {
      context.setDefaultJdbcDriver(driver)
    }
    for (url <- parameters.getString("g3.jdbc.url")) {
      context.setDefaultJdbcUrl(url)
    }
    context.setParameters(parameters)
    context.open()
    _application_session = Some(G3Session(SystemUser))
  }

  private def get_parameter_boolean(params: GParameterRepository, key: String, defaultValue: Boolean): Boolean = {
    params.getString(key) match {
      case Some(value) => value.toLowerCase != "false"
      case None => defaultValue
    }
  }

  private def get_parameter_string(params: GParameterRepository, key: String): String = {
    get_parameter_string(params, key, "")
  }

  private def get_parameter_string(params: GParameterRepository, key: String, defaultValue: String): String = {
    params.getString(key) match {
      case Some(value) => value
      case None => defaultValue
    }
  }    

  def close() {
    context.close()
  }

  def commit() {
    context.commit()
  }

  def rollback() {
    context.rollback()
  }

  /*
   * service api
   */
  private val _content_resulter: PartialFunction[AnyRef, AnyRef] = {
    case r: Record => r
    case rs: RecordSet => rs
    case h: Html => h
    case f: AtomFeed => f
    case e: AtomEntry => e
    case r: RestCommand => r
    case r: RestEvent => r
  }

  private val _string_resulter: PartialFunction[AnyRef, AnyRef] = new ResulterBase {
    override def is_Target(r: AnyRef): Boolean = {
      r match {
        case r2: String => true
        case r2: List[_] => !r2.isEmpty && r2.head.isInstanceOf[String]
        case _ => false
      }
    }

    override def get_Target(r: AnyRef): AnyRef = {
      r match {
        case r2: String => r2
        case r2: List[_] => r2.head.asInstanceOf[AnyRef]
      }
    }
  }

  private val _int_resulter: PartialFunction[AnyRef, AnyRef] = new ResulterBase {
    override def is_Target(r: AnyRef): Boolean = {
      r match {
        case r2: Record => true
        case r2: RecordSet => !r2.isEmpty
        case _ => false
      }
    }

    override def get_Target(r: AnyRef): AnyRef = {
      r match {
        case r2: Record => r2
        case r2: RecordSet => r2.head
      }
    }
  }

  private val _record_resulter: PartialFunction[AnyRef, AnyRef] = new ResulterBase {
    override def is_Target(r: AnyRef): Boolean = {
      r match {
        case r2: Record => true
        case r2: RecordSet => !r2.isEmpty
        case _ => false
      }
    }

    override def get_Target(r: AnyRef): Record = {
      r match {
        case r2: Record => r2
        case r2: RecordSet => r2.head
      }
    }
  }

  private val _record_set_resulter: PartialFunction[AnyRef, AnyRef] = new ResulterBase {
    override def is_Target(r: AnyRef): Boolean = {
      r match {
        case r2: Record => true
        case r2: RecordSet => true
        case _ => false
      }
    }

    override def get_Target(r: AnyRef): AnyRef = {
      r match {
        case r2: Record => RecordSet(r2)
        case r2: RecordSet => r2
      }
    }
  }

  private val _atom_feed_resulter: PartialFunction[AnyRef, AnyRef] = {
    case s: String => s // XXX
  }

  private val _atom_entry_resulter: PartialFunction[AnyRef, AnyRef] = {
    case s: String => s // XXX
  }
  
  def get(uri: String, content: AnyRef = null, session: G3Session = null, resulter: PartialFunction[AnyRef, AnyRef] = _content_resulter): Option[AnyRef] = {
    invoke(new Get(uri, lift(content)), lift(session), resulter)
  }

  def getResult[A](uri: String, content: AnyRef = null, session: G3Session = null, resulter: PartialFunction[AnyRef, A]): A = {
    get(uri, content, session, resulter.asInstanceOf[PartialFunction[AnyRef, AnyRef]]).get.asInstanceOf[A]
  }

  def getResultOption[A](uri: String, content: AnyRef = null, session: G3Session = null)(implicit resulter: PartialFunction[AnyRef, A]): Option[A] = {
    get(uri, content, session, resulter.asInstanceOf[PartialFunction[AnyRef, AnyRef]]).asInstanceOf[Option[A]]
  }

  def getResultEither[A](uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, A] = {
    sys.error("not supported yet")
  }

  def getString(uri: String, content: AnyRef = null, session: G3Session = null): String = {
    get(uri, content, session, _string_resulter).get.asInstanceOf[String]
  }

  def getStringOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[String] = {
    get(uri, content, session, _string_resulter).asInstanceOf[Option[String]]
  }

  def getStringEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, String] = {
    sys.error("not supported yet")
  }

  def getInt(uri: String, content: AnyRef = null, session: G3Session = null): Int = {
    get(uri, content, session, _int_resulter).get.asInstanceOf[Int]
  }

  def getIntOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[Int] = {
    get(uri, content, session, _int_resulter).asInstanceOf[Option[Int]]
  }

  def getIntEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, Int] = {
    sys.error("not supported yet")
  }

  def getRecord(uri: String, content: AnyRef = null, session: G3Session = null): Record = {
    get(uri, content, session, _record_resulter).get.asInstanceOf[Record]
  }

  def getRecordOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[Record] = {
    get(uri, content, session, _record_resulter).asInstanceOf[Option[Record]]
  }

  def getRecordEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, Record] = {
    sys.error("not supported yet")
  }

  def getRecordSet(uri: String, content: AnyRef = null, session: G3Session = null): RecordSet = {
    get(uri, content, session, _record_set_resulter).get.asInstanceOf[RecordSet]
  }

  def getRecordSetOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[RecordSet] = {
    get(uri, content, session, _record_set_resulter).asInstanceOf[Option[RecordSet]]
  }

  def getRecordSetEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, RecordSet] = {
    sys.error("not supported yet")
  }

  def getAtomFeed(uri: String, content: AnyRef = null, session: G3Session = null): AtomFeed = {
    get(uri, content, session, _atom_feed_resulter).get.asInstanceOf[AtomFeed]
  }

  def getAtomFeedOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[AtomFeed] = {
    get(uri, content, session, _atom_feed_resulter).asInstanceOf[Option[AtomFeed]]
  }

  def getAtomFeedEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, AtomFeed] = {
    sys.error("not supported yet")
  }

  def getAtomEntry(uri: String, content: AnyRef = null, session: G3Session = null): AtomEntry = {
    get(uri, content, session, _atom_entry_resulter).get.asInstanceOf[AtomEntry]
  }

  def getAtomEntryOption(uri: String, content: AnyRef = null, session: G3Session = null): Option[AtomEntry] = {
    get(uri, content, session, _atom_entry_resulter).asInstanceOf[Option[AtomEntry]]
  }

  def getAtomEntryEither(uri: String, content: AnyRef = null, session: G3Session = null): Either[Exception, AtomEntry] = {
    sys.error("not supported yet")
  }

  def post(uri: String, content: AnyRef = null, session: G3Session = null, resulter: PartialFunction[AnyRef, AnyRef] = _content_resulter): Option[AnyRef] = {
    invoke(new Post(uri, lift(content)), lift(session), resulter)
  }

  def put(uri: String, content: AnyRef = null, session: G3Session = null, resulter: PartialFunction[AnyRef, AnyRef] = _content_resulter): Option[AnyRef] = {
    invoke(new Put(uri, lift(content)), lift(session), resulter)
  }

  def delete(uri: String, content: AnyRef = null, session: G3Session = null)(resulter: PartialFunction[AnyRef, AnyRef] = _content_resulter): Option[AnyRef] = {
    invoke(new Delete(uri, lift(content)), lift(session), resulter)
  }

  def invoke(command: G3Command, session: Option[G3Session] = None, resulter: PartialFunction[AnyRef, AnyRef] = _content_resulter): Option[AnyRef] = {
    context.findPortChannel(command.uri) match {
      case Some(port) => {
        val ticket = port.stimulus(command, session.getOrElse(_application_session.get))
        val parcel = context.resultParcel(ticket)
//      println("execute:" + parcel.content + "," + cmd.request)
        val ctx = G3AgentContext(parcel, parcel.context)
        G3Actionable.do_action(resulter)(parcel.content, parcel, ctx)
      }
      case None => error("Unknown port = " + command.uri) // FIXME in case of unexists port
    }
  }

  def timer() { // XXX beatTimer?
    val time = System.currentTimeMillis.asInstanceOf[AnyRef]

    for (channel <- context.findTimerChannels()) {
      channel.stimulus(Post("/timer", time), _application_session.get) // XXX timer command object. "/timer" is unused.
    }
  }

  def run(args: AnyRef*): Any = {
    val result = execute(args: _*)
    commit()
    if (param_server) {
      server()
    } else {
      result
    }
  }

  /**
   * Uses application default session.
   */
  def execute(args: AnyRef*): Any = executeWithResulter(args.toList) {
    case c: CliCommand => ""
    case h: Html => h
//    case af: AtomFeed => af
//    case ae: AtomEntry => ae
//    case rs: RecordSet => rs
//    case r: Record => r
    case s: String => s
  }

  /**
   * Uses application default session.
   */
  def executeWithResulter(args: List[AnyRef])(resulter: PartialFunction[AnyRef, AnyRef]): Any = {
    def execute_port(port: G3Channel) = {
      port.stimulus(CliCommand(args),
                    _application_session.get)
    }

    val ticket = context.findPortChannel(args) match {
      case Some(port) => execute_port(port)
      case None => execute_port(start)
    }
    val parcel = context.resultParcel(ticket)
    val ctx = G3AgentContext(parcel, parcel.context)
//    println("G3App: " + parcel.content)
//    println("G3Application exec = " + G3Actionable.do_action(resulter)(parcel.content, parcel, ctx))
    val r = G3Actionable.do_action(resulter)(parcel.content, parcel, ctx) match {
      case Some("") => None
      case Some(r) => r
      case None => parcel.content
    }
//    println("execute_port: " + r)
    r
/* 2010-09-16
    if (resulter.isDefinedAt(parcel)) Some(resulter(parcel))
    else {
      val content = parcel.content
      if (resulter.isDefinedAt(content)) Some(resulter(content))
      else {
        content match {
          case get: Getted => {
            if (resulter.isDefinedAt(get.content)) Some(resulter(get.content))
            else None
          }
          case _ => None
        }
      }
    }
*/
  }

  def executeFeed(args: AnyRef) = {
    // TODO
  }

  val htmlType = "text/html;charset=utf-8" // XXX
  val atomType = "text/xml" // XXX

  def executeHttpResponse(cmd: HttpCommand, resp: HttpServletResponse, session: Option[G3Session] = None) {
    try {
      val response = executeHttp(cmd)
      commit()
      write_response(response, resp)
    } catch {
      case e: Throwable => rollback(); throw e
    }
  }

  private def write_response(response: G3HttpResponse,
                             resp: HttpServletResponse) {
    response.setResponse(resp)
//    resp.setContentType(response.contentType)
//    resp.setStatus(response.statusCode)
//    response match {
//      case s: StringHttpResponse => resp.getWriter().println(s.content)
//      case b: ByteArrayHttpResponse => resp.getOutputStream().write(b.content, 0, b.content.length)
//    }
  }

  // TODO binary output
  def executeHttp(cmd: HttpCommand, session: Option[G3Session] = None): G3HttpResponse = {
    def executeorform(port: PortChannel): G3HttpResponse = {
      if (port.isAccept(cmd)) {
        execute_port(port)
      } else if (port.isPartialAccept(cmd)) {
        htmlpage(_warning_form_page(port, cmd))
      } else {
        htmlpage(_form_page(port))
      }
    }

    def execute_port(port: G3Channel): G3HttpResponse = {
      val ticket = port.stimulus(cmd, session.getOrElse(_application_session.get))
      val parcel = context.resultParcel(ticket)
//      println("executeHttp:" + parcel.content + "," + cmd.request)
      val ctx = G3AgentContext(parcel, parcel.context)
      val result = G3Actionable.do_action(ctx.httpConvertor(cmd))(parcel.content, parcel, ctx)
      result match {
        case Some(response: G3HttpResponse) => response
        case Some((c: String, t: String, sc: Int)) => new StringHttpResponse(c, t, sc)
        case Some((c: String, t: String)) => new StringHttpResponse(c, t, SC_OK)
        case Some(r) => new StringHttpResponse(Html(cmd.uri, r.toString).toText(ctx), htmlType, SC_OK)
        case None => new StringHttpResponse(Html(cmd.uri, parcel.content.toString).toText(ctx), htmlType, SC_OK)
        case _ => new StringHttpResponse("ExecuteHttp", "text/plain", SC_OK) // XXX
      }
/*
      val r = G3Actionable.do_action {
        case h: Html => (h.toText(ctx), htmlType)
        case f: AtomFeed => (f.toText, atomType)
        case e: AtomEntry => (e.toText, atomType)
//        case r: Record => Html.withRecord(cmd.uri, "XXX:Record", r)
//        case rs: RecordSet => Html.withRecordSet(cmd.uri, "XXX:RecordSet", rs)
        case s: String => (Html(cmd.uri, s).toText(ctx), htmlType)
      } (parcel.content, parcel, ctx) match {
        case Some((c: String, t: String)) => (c, t)
        case Some(r) => (Html(cmd.uri, r.toString).toText(ctx), htmlType)
        case None => (Html(cmd.uri, parcel.content.toString).toText(ctx), htmlType)
      }
//      println("execute Port:" + r)
      r
*/
    }

    def not_found(uri: String) = Http404(uri)

    def internal_server_error(
      uri: String, e: Exception = null
    ) = Http500(uri, e)

    def htmlpage(html: Html) = {
      val ctx = new G3AgentContext(None, None, None, None, None, None, context)
      new StringHttpResponse(html.toText(ctx), htmlType, SC_OK)
    } 

    def make_response(evt: HttpStatusEvent): G3HttpResponse = {
      val format = {
        if (cmd.uri.endsWith(".atom")) "atom"
        else if (cmd.uri.endsWith(".json")) "json"
        else if (cmd.uri.endsWith(".csv")) "csv"
        else if (cmd.uri.endsWith(".html")) "html"
        else context.getDefaultHttpFormat
      }
      format match {
        case "atom" => evt.toAtom
        case "json" => evt.toJson(context, cmd)
        case "html" => evt.toHtml
        case _ => evt.toHtml
      }
    }

    try {
      context.findPortChannel(cmd.uri) match {
        case Some(port) => executeorform(port.asInstanceOf[PortChannel])
        case None => {
          context.findPage(cmd.uri) match {
            case Some(response) => response
            case None => if (cmd.uri == "/" || cmd.uri == "/index.html") {
              htmlpage(_top_page) 
            } else {
              make_response(not_found(cmd.uri))
            }
          }
        }
      }
    } catch {
      case e: NotFoundException => make_response(Http404(cmd.uri, e))
      case e: Exception => {
        make_response(internal_server_error(cmd.uri, e))
      }
    }
  }

  private def _top_page: Html = {
    val titlelabel = Option(title).map(_.toXml).toList
    val summarylabel = Option(summary).map(_.toXml).toList
    def servicetable = {
<table><thead><tr><th>Path</th><th>Title</th><th>Sub Title</th></tr></thead><tbody>{
  for (pc <- context.portChannels) yield {
<tr><td><a href={pc.relativePath}>{pc.relativePath}</a></td><td>{pc.titleAsXml}</td><td>{pc.subtitleAsXml}</td></tr>
  }
}</tbody></table>

    }
    Html(title, <div>
<h2>{titlelabel}</h2>
<div>{summarylabel}</div>
<div>{servicetable}</div>
</div>)
  }

  private def _warning_form_page(port: PortChannel, cmd: RestCommand): Html = {
    _form_page(port) // XXX
  }  

  private def _form_page(port: PortChannel): Html = {
    def formaction = {
      if (port.path.startsWith("/")) port.path.substring(1)
      else port.path
    }

    def forminputs: Seq[Node] = {
      def columns = {
        (for (desc <- port.description;schema <- desc.schema) yield {
          for (f <- schema.fields) yield {
            <tr><td>{_field_name_with_annotation(f)}</td><td><input type={_input_type(f)} name={f.name.name} /></td></tr>
          }
        }).orZero
      }

      <table>{columns}</table>
    }

    Html(port.title, <div style={_form_style}><form method="POST" action={formaction} enctype="multipart/form-data">
{forminputs}
<input type="submit" value={_submit_label} />
</form></div>)
  }

  private def _field_name_with_annotation(field: RecordField) = {
    def annotation = {
      field.multiplicity match {
        case MOne => "(*)"
        case MOneMore => "(*)"
        case _ => ""
      }
    }

    field.name.name + annotation
  }

  private def _input_type(field: RecordField) = {
    field.datatype match {
      case _: XBase64Binary => "file"
      case _: XHexBinary => "file"
      case _ => "text"
    }
  }

  private def _form_style = "" // "border: solid 2px black;border-radius: 10px;padding: 10px"
  private def _submit_label = "Submit"

  def server() {
    servers.TimerServer.start(context, Some(this))
    // JettyServer doesn't return
    JettyServer.start(context, Some(this))
  }
}

abstract class G3Node(val atom: Symbol, val context: G3Context) {
  val children = new ArrayBuffer[G3Node]

  def name = atom.name

  def +=(aNode: G3Node) {
    children += aNode
  }

  final def open() {
    children.foreach(_.open())
    open_Node()
  }

  protected def open_Node() {
  }

  final def close() {
    children.foreach(_.close())
    close_Node()
  }

  protected def close_Node() {
  }
}

class G3RootNode(context: G3Context) extends G3Node(Symbol(""), context) {
}

class G3ChannelNode(atom: Symbol, val channel: G3Channel, context: G3Context) extends G3Node(atom, context) {
  override def open_Node() {
    channel.open()
  }

  override def close_Node() {
    channel.close()
  }

/*
  def send(parcel: G3Parcel): G3Ticket = {
    channel.send(parcel)
  }

  def invoke(parcel: G3Parcel): G3Parcel = {
    channel.invoke(parcel)
  }
*/
}
